Nano Hash - криптовалюты, майнинг, программирование

Пользовательское поведение CoordinatorLayout с AppBarLayout

Я пытаюсь добиться аналогичного поведения Telegram на странице настроек, то есть есть CircleImage, который при прокрутке вверх идет слева от заголовка Topbar, а при прокрутке вниз идет в середину развернутого AppBarLayout .

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

Я основывал свою работу на этом примере:

https://github.com/saulmm/CoordinatorBehaviorExample

Но в этом случае исходный кодер дважды воссоздает верхнюю панель. Я не хочу этого делать, поведение верхней панели по умолчанию — это то, что мне нужно, а также я хочу воспользоваться меню гамбургеров и меню параметров, которые поставляются из коробки.

Это моя иерархия представлений:

DrawerLayout
  |
  |---CoordinatorLayout
       |--AppBarLayout
       |    |-CollapsingToolbarLayout
       |        |-ImageView (backdrop image)
       |        |-Toolbar
       |--NestedScrollView
       |--ImageView (circleimage avatar)

Как видите, я не могу сделать макет панели инструментов родственным моему CircleImage, поэтому я не могу связать их вместе с помощью метода layoutDependsOn. Я попытался выполнить привязку к AppBarLayout, взяв за основу код из репозитория github, но, честно говоря, я не могу понять, что происходит в исходном коде.


  • Проверьте это: saulmm.github.io/mastering-coordinator и прокрутите до Заголовок Custom Behavior, вы найдете предполагаемое поведение. 12.11.2016

Ответы:


1

Мое поведение было реализовано почти так же, как и у Сола. Самая большая разница заключается в том, что мне нравится размещать невидимое представление, например Space, где я хотел, чтобы изображение круга заканчивалось, а затем использовать границы этого представления, чтобы определить, как перемещать и размер изображения круга.

public class CollapsingImageBehavior extends CoordinatorLayout.Behavior<View> {

    private final static int X = 0;
    private final static int Y = 1;
    private final static int WIDTH = 2;
    private final static int HEIGHT = 3;

    private int mTargetId;

    private int[] mView;

    private int[] mTarget;

    public CollapsingImageBehavior() {
    }

    public CollapsingImageBehavior(Context context, AttributeSet attrs) {

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingImageBehavior);
            mTargetId = a.getResourceId(R.styleable.CollapsingImageBehavior_collapsedTarget, 0);
            a.recycle();
        }

        if (mTargetId == 0) {
            throw new IllegalStateException("collapsedTarget attribute not specified on view for behavior");
        }
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {

        setup(parent, child);

        AppBarLayout appBarLayout = (AppBarLayout) dependency;

        int range = appBarLayout.getTotalScrollRange();
        float factor = -appBarLayout.getY() / range;

        int left = mView[X] + (int) (factor * (mTarget[X] - mView[X]));
        int top = mView[Y] + (int) (factor * (mTarget[Y] - mView[Y]));
        int width = mView[WIDTH] + (int) (factor * (mTarget[WIDTH] - mView[WIDTH]));
        int height = mView[HEIGHT] + (int) (factor * (mTarget[HEIGHT] - mView[HEIGHT]));

        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.width = width;
        lp.height = height;
        child.setLayoutParams(lp);
        child.setX(left);
        child.setY(top);

        return true;
    }

    private void setup(CoordinatorLayout parent, View child) {

        if (mView != null) return;

        mView = new int[4];
        mTarget = new int[4];

        mView[X] = (int) child.getX();
        mView[Y] = (int) child.getY();
        mView[WIDTH] = child.getWidth();
        mView[HEIGHT] = child.getHeight();

        View target = parent.findViewById(mTargetId);
        if (target == null) {
            throw new IllegalStateException("target view not found");
        }

        mTarget[WIDTH] += target.getWidth();
        mTarget[HEIGHT] += target.getHeight();

        View view = target;
        while (view != parent) {
            mTarget[X] += (int) view.getX();
            mTarget[Y] += (int) view.getY();
            view = (View) view.getParent();
        }

    }
}

А вот и макет. Одна важная вещь, которую я обнаружил, заключается в том, что для представления кругового изображения необходимо установить высоту, чтобы оно располагалось поверх панели инструментов в свернутом режиме, иначе оно было бы за панелью инструментов и не отображалось.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.krislarson.customcoordinatorlayoutbehavior.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="280dp"
            android:minHeight="108dp"
            android:fitsSystemWindows="true"
            app:title="Abby"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="center_horizontal"
            app:expandedTitleMarginTop="140dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/background"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/sunset"
                app:layout_collapseMode="parallax"
                android:scaleType="centerCrop"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay">

                <Space
                    android:id="@+id/circle_collapsed_target"
                    android:layout_width="40dp"
                    android:layout_height="40dp"/>

            </android.support.v7.widget.Toolbar>


        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_scrolling"/>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/circle_image_view"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/abby"
        android:layout_marginTop="220dp"
        android:layout_gravity="top|center_horizontal"
        android:elevation="8dp"
        app:border_color="@android:color/black"
        app:border_width="2dp"
        app:collapsedTarget="@id/circle_collapsed_target"
        app:layout_behavior="com.krislarson.customcoordinatorlayoutbehavior.CollapsingImageBehavior"/>

</android.support.design.widget.CoordinatorLayout>

Весь демонстрационный проект можно увидеть по адресу https://github.com/klarson2/CustomCoordinatorLayoutBehavior.

11.11.2016
  • Мне нужно немного изменить макет, изображение круга начинается в разных местах до и после леденца на палочке. 12.11.2016
  • что такое ‹include layout=@layout/content_scrolling/› ?? andtools:context=com.krislarson.customcoordinatorlayoutbehavior.ScrollingActivity› ?? 12.11.2016
  • Он включает в себя представление прокрутки, которое управляет вложенной прокруткой. Если вы выберете шаблон действия прокрутки при создании нового проекта, вы получите этот XML-файл (activity_scrolling), а также включаемый файл content_scrolling. 12.11.2016
  • Спасибо, это потрясающий ответ. Вы также знаете, каков расширенный размер высоты AppBarLayout по умолчанию? Я пытаюсь правильно расположить CircleImage и не хотел бы жестко кодировать значения. Что-то вроде ?attr/actionBarSize, но для расширенной панели инструментов? 14.11.2016
  • Я не думаю, что существует значение по умолчанию, и я никогда не видел для него атрибута. В гибком пространстве с примером изображения на material.google.com/ Patterns/ говорит «Используйте гибкое пространство для размещения изображений на панели приложений с желаемым соотношением сторон». Поэтому я думаю, что вы основываете высоту на содержимом внутри панели приложения. 15.11.2016
  • Все работает отлично, но есть одно но... при полном сворачивании мой вид изображения круга исчезает. Любые идеи? Я все перепроверил, все похоже на твое. Я клонировал ваше репо, и ваше работает нормально, поэтому в моем что-то не так. 05.12.2017
  • О, ну... Теперь я знаю, что app:elevation — это не то же самое, что android:elevation. Я не верну этот час. :( 06.12.2017
  • @krislarson, ты нашел способ иметь одинаковую начальную позицию для своего круга как до, так и после леденца на палочке? У меня такая же проблема с поведением. 22.05.2018
  • @alexbchr Я пересмотрел свой проект и немного покопался. Несоответствие связано с android:fitsSystemWindows="true" и с тем, как с ним работают компоненты Material Design. IIRC, Android до Lollypop не может соблюдать этот параметр. Так что замеры немного не те. Вы увидите, что у меня есть marginTop, установленный для CircleImageView, который предполагает, что AppBarLayout не отображается под строкой состояния, поэтому я думаю, что способ исправить это — добавить значение @dimen/circle_offset к ресурсам, настроенным на 220dp до Lollypop и 220dp минус ‹высота строки состояния› для Lollypop и более поздних версий. 23.05.2018
  • @krislarson спасибо, что покопались, мне показалось, что проблема была вызвана изменением размера содержащего фрагмента после второго прохода макета. Я просто изменил гравитацию моего взгляда, на который влияет поведение, чтобы он был сверху, а не снизу, так что теперь y-перевод правильно рассчитывается с первого раза. 24.05.2018

  • 2

    Одной из возможностей было бы создать собственное представление для вашего ToolBar и скрыть красную точку в ToolBar, если оно развернуто, и вместо этого показать ImageView с красной точкой (которая скрыта, когда панель инструментов свернута).

    Вы можете увидеть, как добавить настраиваемое представление в ToolBar в этом ответе: https://stackoverflow.com/a/27859966/5052976< /а>

    После этого просто создайте ImageView, который будет виден, когда ToolBar развернут.

    final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
    AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
    boolean isShow = false;
    int scrollRange = -1;
    
    @Override
    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
        if (scrollRange == -1) {
            scrollRange = appBarLayout.getTotalScrollRange();
        }
        if (scrollRange + verticalOffset == 0) {
            //show toolbar dot and hide imageview dot
            isShow = true;
        } else if(isShow) {
            //hide toolbar dot and show imageview dot
            isShow = false;
        }
    }
    });
    

    К сожалению, я не могу проверить это прямо сейчас, но я думаю, что это должно работать ;-)

    11.11.2016
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..