将Android导航抽屉切换图标放置在右侧

17

以下是我的要求

我的导航抽屉应该从右侧打开。我已经实现了这一点,我的导航抽屉从右到左打开。但问题是切换图标始终在左侧。如何将切换图标设置为右侧?

我已检查了以下 SO 问题,但它们都没有帮助:

更改导航抽屉中切换按钮的图像图标从右到左

右边的抽屉中的抽屉切换

进入链接说明此处

输入图片说明

以下是我尝试过的内容:

我的布局代码activity_main.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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="end">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context="com.example.nav.MainActivity"
        android:foregroundGravity="right">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="end"
            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"
                android:layout_gravity="right"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                android:foregroundGravity="right"
                android:textAlignment="viewEnd"
                android:touchscreenBlocksFocus="false" />

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

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

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

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_gravity="end"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/menu_navigation"
        android:textAlignment="viewEnd" />


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

我的活动代码

public class MainActivity extends AppCompatActivity {
    private DrawerLayout drawerLayout;
    private Toolbar toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        initNavigationDrawer();

    }

    @TargetApi(Build.VERSION_CODES.M)
    public void initNavigationDrawer() {

        NavigationView navigationView = (NavigationView)findViewById(R.id.navigation_view);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {

                int id = menuItem.getItemId();

                switch (id){
                    case R.id.home:
                        Toast.makeText(getApplicationContext(),"Home",Toast.LENGTH_SHORT).show();
                        drawerLayout.closeDrawers();
                        break;
                    case R.id.settings:
                        Toast.makeText(getApplicationContext(),"Settings",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.trash:
                        Toast.makeText(getApplicationContext(),"Trash",Toast.LENGTH_SHORT).show();
                        drawerLayout.closeDrawers();
                        break;
                    case R.id.logout:
                        finish();

                }
                return true;
            }
        });
        drawerLayout = (DrawerLayout)findViewById(R.id.drawer);

        ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.drawer_open,R.string.drawer_close){

            @Override
            public void onDrawerClosed(View v){
                super.onDrawerClosed(v);
            }

            @Override
            public void onDrawerOpened(View v) {
                super.onDrawerOpened(v);
            }

            @Override
            public boolean onOptionsItemSelected(MenuItem item) {
                if (item != null && item.getItemId() == android.R.id.home) {
                    if (drawerLayout.isDrawerOpen(Gravity.RIGHT)) {
                        drawerLayout.closeDrawer(Gravity.RIGHT);
                    }
                    else {
                        drawerLayout.openDrawer(Gravity.RIGHT);
                    }
                }
                return false;
            }
        };
        drawerLayout.addDrawerListener(actionBarDrawerToggle);
        actionBarDrawerToggle.syncState();

        toolbar.setNavigationOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (drawerLayout.isDrawerOpen(Gravity.RIGHT)) {
                    drawerLayout.closeDrawer(Gravity.RIGHT);
                } else {
                    drawerLayout.openDrawer(Gravity.RIGHT);
                }
            }
        });
    }

}
2个回答

44

实际上,没有(可行的)方法可以让 ActionBarDrawerToggle 这样做,因为它总是设置在开始/左侧导航按钮上。然而,那个类基本上只是一个管理特定 Drawable 并将 ImageButton 连接到 DrawerLayoutDrawerListener。我们可以为一个末端/右侧抽屉制作类似的东西,其中包含一个 ImageButton,我们可以将其放置在同一侧的 Toolbar 中(这是此示例所必需的)。

public class EndDrawerToggle implements DrawerLayout.DrawerListener {

    private final DrawerLayout drawerLayout;
    private final AppCompatImageButton toggleButton;
    private final int openDrawerContentDescRes;
    private final int closeDrawerContentDescRes;

    private DrawerArrowDrawable arrowDrawable;

    public EndDrawerToggle(DrawerLayout drawerLayout, Toolbar toolbar,
                           int openDrawerContentDescRes, int closeDrawerContentDescRes) {
        this.drawerLayout = drawerLayout;
        this.openDrawerContentDescRes = openDrawerContentDescRes;
        this.closeDrawerContentDescRes = closeDrawerContentDescRes;

        toggleButton = new AppCompatImageButton(toolbar.getContext(), null,
                R.attr.toolbarNavigationButtonStyle);
        toolbar.addView(toggleButton, new Toolbar.LayoutParams(GravityCompat.END));
        toggleButton.setOnClickListener(v -> toggle());

        loadDrawerArrowDrawable();
    }

    public void syncState() {
        if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
            setPosition(1f);
        } else {
            setPosition(0f);
        }
    }

    public void onConfigurationChanged(Configuration newConfig) {
        loadDrawerArrowDrawable();
        syncState();
    }

    @Override
    public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
        setPosition(Math.min(1f, Math.max(0f, slideOffset)));
    }

    @Override
    public void onDrawerOpened(@NonNull View drawerView) {
        setPosition(1f);
    }

    @Override
    public void onDrawerClosed(@NonNull View drawerView) {
        setPosition(0f);
    }

    @Override
    public void onDrawerStateChanged(int newState) {}

    private void loadDrawerArrowDrawable() {
        arrowDrawable = new DrawerArrowDrawable(toggleButton.getContext());
        arrowDrawable.setDirection(DrawerArrowDrawable.ARROW_DIRECTION_END);
        toggleButton.setImageDrawable(arrowDrawable);
    }

    private void toggle() {
        final int drawerLockMode = drawerLayout.getDrawerLockMode(GravityCompat.END);
        if (drawerLayout.isDrawerVisible(GravityCompat.END)
                && (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {
            drawerLayout.closeDrawer(GravityCompat.END);
        } else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
            drawerLayout.openDrawer(GravityCompat.END);
        }
    }

    private void setPosition(float position) {
        if (position == 1f) {
            arrowDrawable.setVerticalMirror(true);
            setContentDescription(closeDrawerContentDescRes);
        } else if (position == 0f) {
            arrowDrawable.setVerticalMirror(false);
            setContentDescription(openDrawerContentDescRes);
        }
        arrowDrawable.setProgress(position);
    }

    private void setContentDescription(int resId) {
        toggleButton.setContentDescription(toggleButton.getContext().getText(resId));
    }
}
EndDrawerToggle类的使用方式与在Toolbar中使用ActionBarDrawerToggle时完全相同(除了构造函数调用不需要一个Activity参数):首先实例化切换,然后将其添加为DrawerListener,并在ActivityonPostCreate()方法中进行同步。如果您已经重写ActivityonConfigurationChanged()方法,则需要在那里调用切换的相应方法,就像对于ActionBarDrawerToggle一样。
private EndDrawerToggle drawerToggle;

public void initNavigationDrawer() {
    ...

    drawerLayout = (DrawerLayout) findViewById(R.id.drawer);

    drawerToggle = new EndDrawerToggle(drawerLayout,
                                       toolbar,
                                       R.string.drawer_open,
                                       R.string.drawer_close);

    drawerLayout.addDrawerListener(drawerToggle);
}

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    drawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    drawerToggle.onConfigurationChanged(newConfig);
}
如果您有两个抽屉并且需要同时使用 ActionBarDrawerToggleEndDrawerToggle,这是可能的,但我们需要处理拦截和分派抽屉运动事件到正确的切换器。
如果您喜欢更少的类,可以子类化 ActionBarDrawerToggle 并将 EndDrawerToggle 的功能合并到其中,将每个 DrawerListener 方法调用分派到 super 类或本地端点切换器代码。
然而,组合在这里可能更加清晰,并且它将让我们直接使用原始的 EndDrawerToggle。这个示例是一个 DrawerListener,它将 syncState()onConfigurationChanged() 调用中继到每个切换器,但只将监听器方法调用分派到相应的抽屉移动。
public class DualDrawerToggle implements DrawerLayout.DrawerListener {

    private final DrawerLayout drawerLayout;
    private final Toolbar toolbar;
    private final ActionBarDrawerToggle actionBarDrawerToggle;
    private final EndDrawerToggle endDrawerToggle;

    public DualDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar,
                            int startDrawerOpenContDescRes, int startDrawerCloseContDescRes,
                            int endDrawerOpenContDescRes, int endDrawerCloseContDescRes) {
        this.drawerLayout = drawerLayout;
        this.toolbar = toolbar;
        this.actionBarDrawerToggle =
                new ActionBarDrawerToggle(activity, drawerLayout, toolbar,
                        startDrawerOpenContDescRes, startDrawerCloseContDescRes);
        this.endDrawerToggle =
                new EndDrawerToggle(drawerLayout, toolbar,
                        endDrawerOpenContDescRes, endDrawerCloseContDescRes);
    }

    public void syncState() {
        actionBarDrawerToggle.syncState();
        endDrawerToggle.syncState();
    }

    public void onConfigurationChanged(Configuration newConfig) {
        actionBarDrawerToggle.onConfigurationChanged(newConfig);
        // Fixes bug in ABDT, which only reloads the up nav indicator, for some reason.
        final DrawerArrowDrawable dad = new DrawerArrowDrawable(toolbar.getContext());
        actionBarDrawerToggle.setDrawerArrowDrawable(dad);

        endDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
        if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
            actionBarDrawerToggle.onDrawerSlide(drawerView, slideOffset);
        } else {
            endDrawerToggle.onDrawerSlide(drawerView, slideOffset);
        }
    }

    @Override
    public void onDrawerOpened(@NonNull View drawerView) {
        if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
            actionBarDrawerToggle.onDrawerOpened(drawerView);
        } else {
            endDrawerToggle.onDrawerOpened(drawerView);
        }
    }

    @Override
    public void onDrawerClosed(@NonNull View drawerView) {
        if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
            actionBarDrawerToggle.onDrawerClosed(drawerView);
        } else {
            endDrawerToggle.onDrawerClosed(drawerView);
        }
    }

    @Override
    public void onDrawerStateChanged(int newState) {}

    @SuppressLint("RtlHardcoded")
    static boolean isStartDrawerView(View drawerView, int layoutDirection) {
        final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
        final int horizontalGravity = gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
        if ((horizontalGravity & GravityCompat.RELATIVE_LAYOUT_DIRECTION) > 0) {
            return horizontalGravity == GravityCompat.START;
        } else {
            if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
                return horizontalGravity == Gravity.RIGHT;
            } else {
                return horizontalGravity == Gravity.LEFT;
            }
        }
    }
}

DualDrawerToggleActionBarDrawerToggle在使用Toolbar时的工作方式完全相同,唯一不同的是构造函数中有额外的内容描述资源ID。

需要注意的是,DualDrawerToggle会在内部创建和管理另外两个toggle。您不需要在Activity中设置任何其他toggle实例,只需设置DualDrawerToggle即可。

EndDrawerToggle(以及因此也是DualDrawerToggle)可以很容易地适应几乎任何可以显示Drawable并注册点击的组件上,例如FloatingActionButton、选项菜单项、TextView的复合图形等。只需将目标UI组件替换为AppCompatImageButton,它可以在构造函数中替换Toolbar,因为toggle不再添加到那里。

此外,这也可以修改为适用于起始/左对齐抽屉,完全替换ActionBarDrawerToggle,以便将其toggle放置在这些各种组件上。

这里使用的AppCompatImageButtonToolbar的常规子项。如果您在Toolbar上使用菜单,该菜单将优先于布局,并将toggle向内推。为了保持在外部,您可以像上面描述的那样修改类以将toggle设置为操作菜单项。我在这里中有一个示例。

如果您的设计要求您使用装饰提供的ActionBar而不是自己的Toolbar,那么那个菜单项示例也可能很有用。虽然ActionBarDrawerToggle可以与装饰提供的ActionBar一起使用,但此示例不能直接使用。


2
没问题。很高兴能帮忙。我应该提一下,结束切换按钮只是Toolbar的普通子项,因此如果您添加其他子View或需要下拉菜单,则需要小心处理,您可能需要手动实现以保持对齐正确。提前告知一下。干杯! - Mike M.
非常感谢,但是当使用EndDrawerToggle时,我该如何更改切换图标? - Drunken Daddy
1
@HeisenBerg 我不确定我理解你的问题。你是指你想要完全不同的图标吗?也就是说,除了动画汉堡包箭头之外的其他东西?如果是这样,那么你可以从 EndDrawerToggle 中删除所有的 arrowDrawable 代码,并在 toggleButton 上设置任何你想要的图像。 - Mike M.
@MikeM,功能可行,但我的汉堡图标菜单移到了工具栏的中央,我该如何修复它? - Rembulan Moon
1
@RembulanMoon 我不确定你的意思是什么。如果你在Toolbar中有一个菜单,那么它会自动固定在最后面,而EndDrawerToggle会放在它之后,朝向中心。如果这是你的问题,你可以看看我的回答(链接) - Mike M.

1
在您的Android清单文件中添加以下代码:

</ p>

android:supportsRtl="true"

将以下代码添加到您的应用程序中,例如:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"

然后在你的onCreate方法中,添加这行代码:
getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);

警告:此方法仅适用于SdkVersion 17+,因此如果您的应用程序目标最低SDK版本较低,则必须创建自定义菜单并覆盖OnCreateOptions方法(除非我不知道其他方法,这是完全可能的)。

https://developer.android.com/guide/topics/manifest/application-element.html#supportsrtl


1
很抱歉!对于我的应用程序,它只更改了菜单!不过很高兴你在上面找到了正确的解决方案! - LBJ33
6
这不是正确的答案。你强制整个应用程序默认使用RTL布局来运行。设置 View.LAYOUT_DIRECTION_RTL 会将每个文本视图和编辑框布局为将右侧视为起点,就像在阿拉伯语中一样。 - JaydeepW
这个解决方案在您的应用程序不需要多语言时是好的和快速的。谢谢。 - Reza

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