将 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.contentCoordinatorLayout:

    <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" />

有什么好的方法可以避免 SnackbarAutoCompleteTextView 的下拉列表覆盖吗?

对于这种情况,我可能有解决方案。当然,有一些假设,但也许解决方案适合您。

这里的关键是将 AutoCompleteTextView 放入 CoordinatorLayout 并向其添加自定义 CoordinatorLayout.Behavior

  1. 为您的 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;
        }
    }
    
  2. 覆盖方法layoutDependsOn:

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }
    
  3. 获取对 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;
    }
    
  4. 覆盖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);
    }
    
  5. 将行为应用于 .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();