Search code examples
androidautocompletetextviewandroid-coordinatorlayoutandroid-windowmanagerandroid-snackbar

Place Snackbar at highest z order to avoid from being blocked by AutoCompleteTextView drop down


I have a Snackbar which is as follows:

enter image description here

However, if the drop down of the AutoCompleteTextView is too long, the drop down will block the Snackbar.

enter image description here

As you can see in the above image, the Snackbar is actually showing. However, its visibility is blocked by the long drop down. You can see from the above image

I try to use the following Snackbar code. Adding bringToFront() doesn't help much.

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

Is there any good way, to avoid Snackbar from being covered by AutoCompleteTextView's drop down?


Solution

  • I might have a solution for that case. Of course, there are some assumptions but maybe the solution would suit you.

    The key here is putting AutoCompleteTextView inside CoordinatorLayout and add custom CoordinatorLayout.Behavior to it.

    1. Create appropriate Behavior for your class:

      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. Override a method layoutDependsOn:

      @Override
      public boolean layoutDependsOn(CoordinatorLayout parent, AutoCompleteTextView child, View dependency) {
          return dependency instanceof Snackbar.SnackbarLayout;
      }
      
    3. Get reference to AutoCompleteTextView popup view:

      Unfortunately I haven't found a simple solution for that. However can be done via reflection.

      @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. Override onDependentViewChanged method:

      @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. Apply the behavior to AutocompleteTextView in .xml:

      app:layout_behavior="com.example.package.AutoCompleteTextViewBehaviour"/>
      

    Of course this is a very basic solution, that for example does not animate list height, but I think this is a good start. Here is the full gist.