将 Snackbar 放在最高的 z 顺序以避免被 AutoCompleteTextView 下拉列表阻止
Place Snackbar at highest z order to avoid from being blocked by AutoCompleteTextView drop down
我有一个 Snackbar
如下:
但是,如果AutoCompleteTextView
的下拉框太长,下拉框会挡住Snackbar
。
正如您在上图中所见,Snackbar
实际上正在显示。然而,它的可见性被长长的下拉菜单挡住了。从上图可以看出
我尝试使用下面的Snackbar code
。添加 bringToFront()
并没有多大帮助。
private void showSnackbar(String message) {
Snackbar snackbar
= Snackbar.make(getActivity().findViewById(R.id.content), message, Snackbar.LENGTH_LONG);
snackbar.getView().bringToFront();
snackbar.show();
}
R.id.content
是 CoordinatorLayout
:
<android.support.design.widget.CoordinatorLayout
android:id="@+id/content"
android:background="?attr/MyActivityBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?attr/headerShadow" />
有什么好的方法可以避免 Snackbar
被 AutoCompleteTextView
的下拉列表覆盖吗?
对于这种情况,我可能有解决方案。当然,有一些假设,但也许解决方案适合您。
这里的关键是将 AutoCompleteTextView
放入 CoordinatorLayout
并向其添加自定义 CoordinatorLayout.Behavior
。
为您的 class 创建合适的 Behavior
:
public class AutoCompleteTextViewBehaviour extends CoordinatorLayout.Behavior<AutoCompleteTextView> {
public AutoCompleteTextViewBehaviour(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
}
覆盖方法layoutDependsOn
:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
获取对 AutoCompleteTextView
弹出视图的引用:
不幸的是,我还没有找到一个简单的解决方案。但是可以通过反射来完成。
@Nullable
private View getPopupList(AutoCompleteTextView child) {
try {
Field popupField;
Class clazz;
if (child instanceof AppCompatAutoCompleteTextView) {
clazz = child.getClass().getSuperclass();
} else {
clazz = child.getClass();
}
popupField = clazz.getDeclaredField("mPopup");
popupField.setAccessible(true);
ListPopupWindow popup = (ListPopupWindow) popupField.get(child);
Field popupListViewField = popup.getClass().getDeclaredField("mDropDownList");
popupListViewField.setAccessible(true);
return (View) popupListViewField.get(popup);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
覆盖onDependentViewChanged
方法:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, final AutoCompleteTextView child, View dependency) {
if (popupList == null) {
popupList = getPopupList(child);
if (popupList == null) {
return super.onDependentViewChanged(parent, child, dependency);
}
}
int dropdownBottom = child.getBottom() + child.getDropDownVerticalOffset() + popupList.getHeight();
int snackBarTop = dependency.getTop();
int difference = dropdownBottom - snackBarTop;
if (difference > 0) {
child.setDropDownHeight(popupList.getHeight() - difference);
return true;
} else {
child.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
}
return super.onDependentViewChanged(parent, child, dependency);
}
将行为应用于 .xml
中的 AutocompleteTextView
:
app:layout_behavior="com.example.package.AutoCompleteTextViewBehaviour"/>
当然这是一个非常基本的解决方案,例如不设置列表高度的动画,但我认为这是一个好的开始。这是完整的 gist.
您可以计算并调整弹出窗口的高度作为替代。
在下图中,我将下拉高度设置为如下:
textView.viewTreeObserver.addOnGlobalLayoutListener {
textView.dropDownHeight = snackbarView.top - textView.bottom
}
此计算是针对建议列表的高度足够长的情况。您可能希望将 属性 设置为 WRAP_CONTENT
。
据我所知,除非将 SnackBar 直接添加到 WindowManager
,否则无法更改 "z-order"。 AutoCompleteTextView
内部使用 ListPopupWindow
显示建议弹出窗口,ListPopupWindow
的 window 类型 WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
= 1000 高于 Activity的 Window 类型是 WindowManager.LayoutParams.TYPE_APPLICATION
= 2.
为什么不直接将 android:dropDownHeight
设置为固定的 dp
值?您甚至可以通过引用 dimension
资源将其定义为基于屏幕尺寸动态显示。它是一行代码,它解决了你的问题,易于维护和理解(六个月后你会问自己,整个 Behavior
东西是用来做什么的)。
我觉得你要多方面考虑:
- 所有人都必须 space。键盘、快餐栏和自动完成 Textview
- 您无法更改键盘高度(通过您的应用程序),因此您必须将自动完成 TextView 的高度更改为高于小吃栏(位于键盘上方)
在我的例子中,我可以让它与 setZ
:
一起工作
Snackbar snackbar = Snackbar.make(container, text, Snackbar.LENGTH_LONG);
snackbar.getView().setZ(200);
snackbar.show();
我有一个 Snackbar
如下:
但是,如果AutoCompleteTextView
的下拉框太长,下拉框会挡住Snackbar
。
正如您在上图中所见,Snackbar
实际上正在显示。然而,它的可见性被长长的下拉菜单挡住了。从上图可以看出
我尝试使用下面的Snackbar code
。添加 bringToFront()
并没有多大帮助。
private void showSnackbar(String message) {
Snackbar snackbar
= Snackbar.make(getActivity().findViewById(R.id.content), message, Snackbar.LENGTH_LONG);
snackbar.getView().bringToFront();
snackbar.show();
}
R.id.content
是 CoordinatorLayout
:
<android.support.design.widget.CoordinatorLayout
android:id="@+id/content"
android:background="?attr/MyActivityBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?attr/headerShadow" />
有什么好的方法可以避免 Snackbar
被 AutoCompleteTextView
的下拉列表覆盖吗?
对于这种情况,我可能有解决方案。当然,有一些假设,但也许解决方案适合您。
这里的关键是将 AutoCompleteTextView
放入 CoordinatorLayout
并向其添加自定义 CoordinatorLayout.Behavior
。
为您的 class 创建合适的
Behavior
:public class AutoCompleteTextViewBehaviour extends CoordinatorLayout.Behavior<AutoCompleteTextView> { public AutoCompleteTextViewBehaviour(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; } }
覆盖方法
layoutDependsOn
:@Override public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; }
获取对
AutoCompleteTextView
弹出视图的引用:不幸的是,我还没有找到一个简单的解决方案。但是可以通过反射来完成。
@Nullable private View getPopupList(AutoCompleteTextView child) { try { Field popupField; Class clazz; if (child instanceof AppCompatAutoCompleteTextView) { clazz = child.getClass().getSuperclass(); } else { clazz = child.getClass(); } popupField = clazz.getDeclaredField("mPopup"); popupField.setAccessible(true); ListPopupWindow popup = (ListPopupWindow) popupField.get(child); Field popupListViewField = popup.getClass().getDeclaredField("mDropDownList"); popupListViewField.setAccessible(true); return (View) popupListViewField.get(popup); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; }
覆盖
onDependentViewChanged
方法:@Override public boolean onDependentViewChanged(CoordinatorLayout parent, final AutoCompleteTextView child, View dependency) { if (popupList == null) { popupList = getPopupList(child); if (popupList == null) { return super.onDependentViewChanged(parent, child, dependency); } } int dropdownBottom = child.getBottom() + child.getDropDownVerticalOffset() + popupList.getHeight(); int snackBarTop = dependency.getTop(); int difference = dropdownBottom - snackBarTop; if (difference > 0) { child.setDropDownHeight(popupList.getHeight() - difference); return true; } else { child.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT); } return super.onDependentViewChanged(parent, child, dependency); }
将行为应用于
.xml
中的AutocompleteTextView
:app:layout_behavior="com.example.package.AutoCompleteTextViewBehaviour"/>
当然这是一个非常基本的解决方案,例如不设置列表高度的动画,但我认为这是一个好的开始。这是完整的 gist.
您可以计算并调整弹出窗口的高度作为替代。 在下图中,我将下拉高度设置为如下:
textView.viewTreeObserver.addOnGlobalLayoutListener {
textView.dropDownHeight = snackbarView.top - textView.bottom
}
此计算是针对建议列表的高度足够长的情况。您可能希望将 属性 设置为 WRAP_CONTENT
。
据我所知,除非将 SnackBar 直接添加到 WindowManager
,否则无法更改 "z-order"。 AutoCompleteTextView
内部使用 ListPopupWindow
显示建议弹出窗口,ListPopupWindow
的 window 类型 WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
= 1000 高于 Activity的 Window 类型是 WindowManager.LayoutParams.TYPE_APPLICATION
= 2.
为什么不直接将 android:dropDownHeight
设置为固定的 dp
值?您甚至可以通过引用 dimension
资源将其定义为基于屏幕尺寸动态显示。它是一行代码,它解决了你的问题,易于维护和理解(六个月后你会问自己,整个 Behavior
东西是用来做什么的)。
我觉得你要多方面考虑:
- 所有人都必须 space。键盘、快餐栏和自动完成 Textview
- 您无法更改键盘高度(通过您的应用程序),因此您必须将自动完成 TextView 的高度更改为高于小吃栏(位于键盘上方)
在我的例子中,我可以让它与 setZ
:
Snackbar snackbar = Snackbar.make(container, text, Snackbar.LENGTH_LONG);
snackbar.getView().setZ(200);
snackbar.show();