Поместите Snackbar на самом высоком уровне z, чтобы избежать блокировки выпадающим меню AutoCompleteTextView


У меня есть 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?

6 9

6 ответов:

Возможно, у меня есть решение для этого случая. Конечно, есть некоторые предположения, но, возможно, решение вас устроит.

Ключ здесь заключается в том, чтобы поместить AutoCompleteTextView внутрь CoordinatorLayout и добавить к нему пользовательский CoordinatorLayout.Behavior.

  1. Создайте соответствующий 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. Примените поведение к AutocompleteTextView в .xml:

    app:layout_behavior="com.example.package.AutoCompleteTextViewBehaviour"/>
    
Конечно, это очень простое решение, которое, например, не анимирует высоту списка, но я думаю, что это хорошее начало. Вот полнаясуть .

Вы можете рассчитать и настроить высоту всплывающего окна в качестве альтернативы. На следующем рисунке я устанавливаю высоту выпадающего списка следующим образом:

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

Введите описание изображения здесь

Это вычисление для случая, когда высота списка предложений достаточно велика. Вместо этого вы можете установить для этого свойства значение WRAP_CONTENT.

Насколько мне известно, изменить "z-порядок" невозможно, если вы не добавите закусочную непосредственно к WindowManager. AutoCompleteTextView внутренне использует ListPopupWindow, чтобы показать всплывающее окно предложений и ListPopupWindow имеет тип окна WindowManager.LayoutParams.TYPE_APPLICATION_PANEL = 1000, который выше, чем тип окна действия, который имеет WindowManager.LayoutParams.TYPE_APPLICATION = 2.

Почему бы просто не установить android:dropDownHeight в фиксированное dp значение? Вы даже можете определить его динамически на основе размера экрана, обратившись к ресурсу dimension. Это одна строка кода, она решает вашу проблему, проста в обслуживании и понимании (через полгода вы спросите себя, для чего используется весь материал Behavior).

Я думаю, что вы должны принять во внимание много мыслей:

  • должно быть место для всех. клавиатура, закусочная и автозаполнение Textview
  • Вы не можете изменить высоту клавиатуры (с помощью вашего приложения), поэтому вы должны изменить высоту автозаполнения TextView, чтобы быть выше закусочной (которая находится над клавиатурой)

В моем случае я мог бы заставить его работать с setZ:

Snackbar snackbar = Snackbar.make(container, text, Snackbar.LENGTH_LONG);
snackbar.getView().setZ(200);
snackbar.show();

Шаг 1. Реализовать новое поведение

Необходимо проверить, поддерживает ли операционное устройство snackbar.

public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
    private static final boolean SNACKBAR_BEHAVIOR_ENABLED;

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
        child.setTranslationY(translationY);
        return true;
    }

    static {
        SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
    }
}

Шаг 2. Реализуйте пользовательский вид, чтобы мы могли применить к нему MoveUpwardBehavior.

В этом случае мы заставляем весь LinearLayout взаимодействовать с snackbar. Это очень просто, как здесь сказано, просто передайте класс в defaultbehavior аннотации.

@CoordinatorLayout.DefaultBehavior(MoveUpwardBehavior.class)
public class CustomLinearLayout extends LinearLayout {
    public CustomLinearLayout(Context context) {
        super(context);
    }

    public CustomLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}
Шаг 3. Почти Готово!

Добавьте CustomLinearLayout в макет. Помните, что он должен быть включен Координаторлай!

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.alisondemo.musicgenre.CustomLinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        ...
     </com.example.alisondemo.musicgenre.CustomLinearLayout>
</android.support.design.widget.CoordinatorLayout>

Но....почему нам даже не нужно реализовывать поведение для FAB?

Сформируйте исходный код android.поддержка.дизайн.штучка.Класс FloatingActionButton, как вы можете видеть:

@DefaultBehavior (FloatingActionButton.Поведение.класс) FloatingActionButton общественный класс расширяет графическое представление { ... Да, у него на самом деле реализовано собственное поведение.

Заключение

Мы можем реализовать любой тип поведения, который мы хотим, в любых представлениях. Это должно быть очень интересный. :)

Вы можете увидеть только демо: http://alisonhuang-blog.logdown.com/posts/290009-design-support-library-coordinator-layout-and-behavior

Полный исходный код теперь на GitHub https://github.com/Alishuang/MusicGenre