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

5

9

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?

Rossy answered 31/5, 2017 at 19:40 Comment(10)
Do you consider having part of the drop down covered by the snack bar to be better?? Or are you willing to limit the height of the drop down?Ecology
We prefer not to limit the height of drop down.Rossy
That would prevent the user from clicking the last item...Ecology
I agree with @dev.bmax. It would be a poor UX to have the last item of the drop down blocked or partially hidden. Making the drop down just a little shorter seems like a much better option.Hardee
And if you prefer not to limit the height of the drop down, what if it has 50 items? Wouldn't a scrollable list be fine?Hardee
It's scrollable by default.Ecology
What kind of content is in your snackbar that you don't want hidden?Hardee
Rather than changing the z index of the snackbar, what about trying to make the scrollable list have a shorter horizontal height. I don't know how to do that off hand but even if it is not possible with the built in functionality, you could make your own custom list.Hardee
In theory ListPopupWindow material style has elevation of 16dp, so if you set Snackbar elevation to be bigger by calling snackbar.getView().setElevation([float]), that might work. But will work only for Lollipop+.Whipsaw
@CheokYanCheng, see you, my post. please :)Distiller
A
7

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.

Ajit answered 5/6, 2017 at 17:12 Comment(4)
Can you clarify just, why do you need the reference to mDropDownList? Isn't child.getDropDownHeight() appropriate?Ecology
By default calling the function child.getDropDownHeight() returns -2, which is the equivalent of value android.view.ViewGroup.LayoutParams#WRAP_CONTENT. An exact height is needed.Whipsaw
we set the height of the popup only when the SnackbarLayout changes, right ?Claudianus
Exactly. And only when popup intersects SnackbarWhipsaw
C
5

You can calculate and adjust the height of the popup as an alternative. In the following picture, I am setting the dropdown height as bellow:

textView.viewTreeObserver.addOnGlobalLayoutListener {
  textView.dropDownHeight = snackbarView.top - textView.bottom
}

enter image description here

This calculation is for the case where the suggestion list's height is long enough. You might want to set that property to WRAP_CONTENT instead.

As far as I know, it is not possible to change the "z-order" unless you add the SnackBar directly to the WindowManager. AutoCompleteTextView internally is using ListPopupWindow to show the suggestions popup and ListPopupWindow has the window type of WindowManager.LayoutParams.TYPE_APPLICATION_PANEL = 1000 which is higher than the Activity's Window type which is of WindowManager.LayoutParams.TYPE_APPLICATION = 2.

Claudianus answered 8/6, 2017 at 17:20 Comment(1)
This solution is much concise.Brookner
E
2

In my case I could get it to work with setZ:

Snackbar snackbar = Snackbar.make(container, text, Snackbar.LENGTH_LONG);
snackbar.getView().setZ(200);
snackbar.show();
Ette answered 12/10, 2017 at 20:58 Comment(0)
W
0

Why not just set android:dropDownHeight to a fixed dp value? You can even define it to be dynamically based on screen size by reference a dimension resource. It is one line of code, it solves your problem, easy to maintain and to understand (in six month you will ask yourself, what the whole Behavior stuff is used for).

War answered 9/6, 2017 at 19:57 Comment(0)
S
0

I think you have to take many thinks in consideration:

  • there must be space for all. keyboard, snackbar and autocomplete Textview
  • you cannot change the keyboard height (by your app), so you have to change the height of the autocomplete TextView to be above the snackbar (which is above the keyboard)
Steelworker answered 11/6, 2017 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.