获取对支持操作栏中抽屉切换的引用
Get reference to drawer toggle in support actionbar
我将 ShowcaseView 库用于应用程序教程。我需要获得对导航抽屉切换按钮(又名 "burger button")的引用:
我把Toolbar当成Actionbar使用了,不知道怎么弄这个按钮。通常切换抽屉我用这个:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
toggleDrawer(Gravity.START);
}
}
但是当我使用 Device Monitor 对屏幕进行快照时,没有 ID 为 "home" 的视图。
有什么建议吗?
这就是所谓的导航按钮,它实际上是一个嵌套在 Toolbar
中的 ImageButton
。不幸的是,没有 public 方法或字段来获取对它的引用,因此我们不得不绕行。
对此有几种不同的方法,具有不同程度的有效性和审慎性。任你选。
迭代
如果您可以控制它,并且能够首先设置切换按钮,那么紧接着导航按钮应该是 Toolbar
的第一个(也可能是唯一的)ImageButton
子项.如果您确信它是,那么这可能是最直接的方法。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
for (int i = 0; i < toolbar.getChildCount(); ++i) {
final View child = toolbar.getChildAt(i);
if (child instanceof ImageButton) {
return (ImageButton) child;
}
}
return null;
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
children.firstOrNull { it is ImageButton } as ImageButton?
反光
这种方法的优点是绝对有把握。然而,它是反思,所以,你知道,无论你对此有何看法。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
try {
final Field mNavButtonView =
Toolbar.class.getDeclaredField("mNavButtonView");
mNavButtonView.setAccessible(true);
return (ImageButton) mNavButtonView.get(toolbar);
} catch (Exception e) {
return null;
}
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
try {
Toolbar::class.java
.getDeclaredField("mNavButtonView").apply {
isAccessible = true
}.get(this) as ImageButton?
} catch (e: Exception) {
null
}
按内容描述查找
这是通过为导航按钮的某些 属性 设置特殊值来完成任务的几种方法中的第一种。这个暂时把它的内容描述设置为一个唯一的值,然后用ViewGroup#findViewsWithText()
的方法查找,然后恢复原来的描述。
这个和Find by Tag例子都使用了这个字符串资源,它的值可以是任何你喜欢的,真的:
<string name="toolbar_navigation_button_locator">ToolbarNavigationButtonLocator</string>
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final CharSequence originalDescription =
toolbar.getNavigationContentDescription();
final CharSequence locator =
toolbar.getResources()
.getText(R.string.toolbar_navigation_button_locator);
toolbar.setNavigationContentDescription(locator);
final ArrayList<View> views = new ArrayList<>();
toolbar.findViewsWithText(
views,
locator,
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
toolbar.setNavigationContentDescription(originalDescription);
for (View view : views) {
if (view instanceof ImageButton) {
return (ImageButton) view;
}
}
return null;
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() {
val originalDescription = navigationContentDescription
val locator =
resources.getText(R.string.toolbar_navigation_button_locator)
navigationContentDescription = locator
val views = ArrayList<View>()
findViewsWithText(
views,
locator,
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
)
navigationContentDescription = originalDescription
return views.firstOrNull { it is ImageButton } as ImageButton?
}
按标签查找
此方法利用了能够通过 toolbarNavigationButtonStyle
主题属性设置导航按钮样式的优势。在指定的样式中,我们将android:tag
属性设置为我们唯一的定位符字符串,并在运行时使用View#findViewWithTag()
方法来抓取它。
<style name="Theme.YourApp" parent="...">
...
<item name="toolbarNavigationButtonStyle">@style/Widget.App.Button.Navigation</item>
</style>
<style name="Widget.App.Button.Navigation" parent="Widget.AppCompat.Toolbar.Button.Navigation">
<item name="android:tag">@string/toolbar_navigation_button_locator</item>
</style>
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final CharSequence tag =
toolbar.getResources()
.getText(R.string.toolbar_navigation_button_locator);
return toolbar.findViewWithTag(tag);
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
findViewWithTag(
resources
.getText(R.string.toolbar_navigation_button_locator)
)
按 ID 查找
使用与标记方法相同的样式技术,我们可以改为设置 android:id
属性,并可能使它与熟悉的 findViewById()
功能一起使用。然而,如果他们真的在那个按钮上设置了一个 ID——即使只是供内部使用——这很可能会失败,或者破坏 Toolbar
.
中的某些东西
我们首先定义一个ID来分配按钮:
<item name="navigation_button" type="id" />
然后更改之前的主题设置以设置 ID 而不是标签:
<style name="Theme.YourApp" parent="...">
...
<item name="toolbarNavigationButtonStyle">@style/Widget.App.Button.Navigation</item>
</style>
<style name="Widget.App.Button.Navigation" parent="Widget.AppCompat.Toolbar.Button.Navigation">
<item name="android:id">@id/navigation_button</item>
</style>
为了完整起见:
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
return toolbar.findViewById(R.id.navigation_button);
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() = findViewById(R.id.navigation_button)
可绘制回调
这个利用了 ImageButton
将自己设置为其来源 Drawable
的 Callback
这一事实。我们创建一个throwawayDrawable
临时设置为导航图标,检查Callback
对象是否是我们的ImageButton
,并恢复原来的图标。
我想说,这更像是一种开箱即用的概念验证类型的东西。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final Drawable originalIcon = toolbar.getNavigationIcon();
final ColorDrawable temporaryDrawable = new ColorDrawable(0);
toolbar.setNavigationIcon(temporaryDrawable);
Object callback = temporaryDrawable.getCallback();
toolbar.setNavigationIcon(originalIcon);
if (callback instanceof ImageButton) {
return (ImageButton) callback;
}
else {
return null;
}
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() {
val originalIcon: Drawable? = navigationIcon
val temporaryDrawable = ColorDrawable(0)
navigationIcon = temporaryDrawable
val callback: Any? = temporaryDrawable.callback
navigationIcon = originalIcon
return if (callback is ImageButton) {
callback
} else {
null
}
}
备注:
导航按钮根据需要动态实例化。这意味着某些东西必须设置了一些导航按钮 属性 才能找到它,无论是主题设置还是您自己的代码。对于按内容描述查找、按标签查找和可绘制回调,这不是问题选项,因为它们首先设置这样的 属性。但是,对于 Iterative 和 Reflective 方法,您可能需要注意时间安排。
此答案的先前修订假定某些设置可能使用 Toolbar
的徽标作为切换按钮,而不是导航按钮。这是极不可能的,因此将其提及移至此脚注,如果只是为了将知识保留在可访问的地方。
- 徽标是
ImageView
,并且适用 Iterative 选项中的相同建议。
View
的字段名称是 mLogoView
,可以更改 Reflective 方法来查找它。
Toolbar
class提供了setLogoDescription()
方法,可配合按内容描述查找方法查找logo相反。
- 还有
setLogo()
方法与 Drawable Callback 技术一起使用。
- 按标记查找 选项不适用于徽标
View
。
在您的 Xml 文件中,只需在 toolbar/Layout 的最左侧添加一个视图,然后在您的展示目标
中使用该视图
我将 ShowcaseView 库用于应用程序教程。我需要获得对导航抽屉切换按钮(又名 "burger button")的引用:
我把Toolbar当成Actionbar使用了,不知道怎么弄这个按钮。通常切换抽屉我用这个:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
toggleDrawer(Gravity.START);
}
}
但是当我使用 Device Monitor 对屏幕进行快照时,没有 ID 为 "home" 的视图。
有什么建议吗?
这就是所谓的导航按钮,它实际上是一个嵌套在 Toolbar
中的 ImageButton
。不幸的是,没有 public 方法或字段来获取对它的引用,因此我们不得不绕行。
对此有几种不同的方法,具有不同程度的有效性和审慎性。任你选。
迭代
如果您可以控制它,并且能够首先设置切换按钮,那么紧接着导航按钮应该是 Toolbar
的第一个(也可能是唯一的)ImageButton
子项.如果您确信它是,那么这可能是最直接的方法。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
for (int i = 0; i < toolbar.getChildCount(); ++i) {
final View child = toolbar.getChildAt(i);
if (child instanceof ImageButton) {
return (ImageButton) child;
}
}
return null;
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
children.firstOrNull { it is ImageButton } as ImageButton?
反光
这种方法的优点是绝对有把握。然而,它是反思,所以,你知道,无论你对此有何看法。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
try {
final Field mNavButtonView =
Toolbar.class.getDeclaredField("mNavButtonView");
mNavButtonView.setAccessible(true);
return (ImageButton) mNavButtonView.get(toolbar);
} catch (Exception e) {
return null;
}
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
try {
Toolbar::class.java
.getDeclaredField("mNavButtonView").apply {
isAccessible = true
}.get(this) as ImageButton?
} catch (e: Exception) {
null
}
按内容描述查找
这是通过为导航按钮的某些 属性 设置特殊值来完成任务的几种方法中的第一种。这个暂时把它的内容描述设置为一个唯一的值,然后用ViewGroup#findViewsWithText()
的方法查找,然后恢复原来的描述。
这个和Find by Tag例子都使用了这个字符串资源,它的值可以是任何你喜欢的,真的:
<string name="toolbar_navigation_button_locator">ToolbarNavigationButtonLocator</string>
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final CharSequence originalDescription =
toolbar.getNavigationContentDescription();
final CharSequence locator =
toolbar.getResources()
.getText(R.string.toolbar_navigation_button_locator);
toolbar.setNavigationContentDescription(locator);
final ArrayList<View> views = new ArrayList<>();
toolbar.findViewsWithText(
views,
locator,
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
toolbar.setNavigationContentDescription(originalDescription);
for (View view : views) {
if (view instanceof ImageButton) {
return (ImageButton) view;
}
}
return null;
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() {
val originalDescription = navigationContentDescription
val locator =
resources.getText(R.string.toolbar_navigation_button_locator)
navigationContentDescription = locator
val views = ArrayList<View>()
findViewsWithText(
views,
locator,
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
)
navigationContentDescription = originalDescription
return views.firstOrNull { it is ImageButton } as ImageButton?
}
按标签查找
此方法利用了能够通过 toolbarNavigationButtonStyle
主题属性设置导航按钮样式的优势。在指定的样式中,我们将android:tag
属性设置为我们唯一的定位符字符串,并在运行时使用View#findViewWithTag()
方法来抓取它。
<style name="Theme.YourApp" parent="...">
...
<item name="toolbarNavigationButtonStyle">@style/Widget.App.Button.Navigation</item>
</style>
<style name="Widget.App.Button.Navigation" parent="Widget.AppCompat.Toolbar.Button.Navigation">
<item name="android:tag">@string/toolbar_navigation_button_locator</item>
</style>
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final CharSequence tag =
toolbar.getResources()
.getText(R.string.toolbar_navigation_button_locator);
return toolbar.findViewWithTag(tag);
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() =
findViewWithTag(
resources
.getText(R.string.toolbar_navigation_button_locator)
)
按 ID 查找
使用与标记方法相同的样式技术,我们可以改为设置 android:id
属性,并可能使它与熟悉的 findViewById()
功能一起使用。然而,如果他们真的在那个按钮上设置了一个 ID——即使只是供内部使用——这很可能会失败,或者破坏 Toolbar
.
我们首先定义一个ID来分配按钮:
<item name="navigation_button" type="id" />
然后更改之前的主题设置以设置 ID 而不是标签:
<style name="Theme.YourApp" parent="...">
...
<item name="toolbarNavigationButtonStyle">@style/Widget.App.Button.Navigation</item>
</style>
<style name="Widget.App.Button.Navigation" parent="Widget.AppCompat.Toolbar.Button.Navigation">
<item name="android:id">@id/navigation_button</item>
</style>
为了完整起见:
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
return toolbar.findViewById(R.id.navigation_button);
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() = findViewById(R.id.navigation_button)
可绘制回调
这个利用了 ImageButton
将自己设置为其来源 Drawable
的 Callback
这一事实。我们创建一个throwawayDrawable
临时设置为导航图标,检查Callback
对象是否是我们的ImageButton
,并恢复原来的图标。
我想说,这更像是一种开箱即用的概念验证类型的东西。
Java
static ImageButton getNavigationButton(Toolbar toolbar) {
final Drawable originalIcon = toolbar.getNavigationIcon();
final ColorDrawable temporaryDrawable = new ColorDrawable(0);
toolbar.setNavigationIcon(temporaryDrawable);
Object callback = temporaryDrawable.getCallback();
toolbar.setNavigationIcon(originalIcon);
if (callback instanceof ImageButton) {
return (ImageButton) callback;
}
else {
return null;
}
}
Kotlin
val Toolbar.navigationButton: ImageButton?
get() {
val originalIcon: Drawable? = navigationIcon
val temporaryDrawable = ColorDrawable(0)
navigationIcon = temporaryDrawable
val callback: Any? = temporaryDrawable.callback
navigationIcon = originalIcon
return if (callback is ImageButton) {
callback
} else {
null
}
}
备注:
导航按钮根据需要动态实例化。这意味着某些东西必须设置了一些导航按钮 属性 才能找到它,无论是主题设置还是您自己的代码。对于按内容描述查找、按标签查找和可绘制回调,这不是问题选项,因为它们首先设置这样的 属性。但是,对于 Iterative 和 Reflective 方法,您可能需要注意时间安排。
此答案的先前修订假定某些设置可能使用
Toolbar
的徽标作为切换按钮,而不是导航按钮。这是极不可能的,因此将其提及移至此脚注,如果只是为了将知识保留在可访问的地方。- 徽标是
ImageView
,并且适用 Iterative 选项中的相同建议。 View
的字段名称是mLogoView
,可以更改 Reflective 方法来查找它。Toolbar
class提供了setLogoDescription()
方法,可配合按内容描述查找方法查找logo相反。- 还有
setLogo()
方法与 Drawable Callback 技术一起使用。 - 按标记查找 选项不适用于徽标
View
。
- 徽标是
在您的 Xml 文件中,只需在 toolbar/Layout 的最左侧添加一个视图,然后在您的展示目标
中使用该视图